

use crate::{cmd::{extract_args, validate_command}, Backend, CommandError, CommandExecutor, Get, RespArray, RespFrame, Set, SimpleString};
use lazy_static::lazy_static;
const ONE_ARG: usize = 1;
// const TWO_ARGS: usize = 2;

lazy_static! {
    static ref RESP_OK: RespFrame = SimpleString::new("OK".to_string()).into();
}


impl CommandExecutor for Get{
    fn execute(self, backend: &Backend) -> RespFrame {
        match backend.get(&self.key) {
            Some(value) => value,
            None => RespFrame::Null(crate::RespNull),
        }
    }
}

impl CommandExecutor for Set{
    fn execute(self, backend: &Backend) -> RespFrame {
        backend.set(self.key, self.value);
        RESP_OK.clone()
    }
    
}

impl TryFrom<RespArray> for Get{
    type Error = CommandError;
    fn try_from(value: RespArray) -> Result<Self, Self::Error> {
        validate_command(&value, &["GET"], ONE_ARG)?;
        let agrs = extract_args(&value, ONE_ARG)?;
        match agrs[0] {
            RespFrame::BulkString(key ) => Ok(Get{key: String::from_utf8_lossy(key).to_string(),}),
            _ => Err(CommandError::InvalidArgument("Invalid key".to_string())),
        }
    }
}

impl TryFrom<RespArray> for Set{
    type Error = CommandError;
    fn try_from(value: RespArray) -> Result<Self, Self::Error> {
        // validate_command(&value, &["set"], TWO_ARGS)?;
        let agrs = extract_args(&value, ONE_ARG)?;
        match (agrs[0], agrs[1]) {
            (RespFrame::BulkString(key), value) => Ok(Set{
                key: String::from_utf8_lossy(key).to_string(),
                value: value.clone(),
            }),
            _ => Err(CommandError::InvalidArgument("Invalid key or value".to_string())),
        }
    }
}

#[cfg(test)]
mod tests{
    use bytes::BytesMut;

    use super::*;
    use crate::{RespArray, RespDecode};
    use anyhow::Result;     

    #[test]
    fn test_get_try_from_resp_array() -> Result<()>{
        let mut buf = BytesMut::new();
        buf.extend_from_slice(b"*2\r\n$3\r\nget\r\n$5\r\nhello\r\n");
        let frame = RespArray::decode(&mut buf)?;
        let result: Get = frame.try_into()?;
        assert_eq!(result.key,"hello");

        Ok(())
    }

    #[test]
    fn test_set_try_from_resp_array() -> Result<()>{
        let mut buf = BytesMut::new();
        buf.extend_from_slice(b"*3\r\n$3\r\nset\r\n$5\r\nhello\r\n$5\r\nworld\r\n");
        let frame = RespArray::decode(&mut buf)?;
        let result: Set = frame.try_into()?;
        // assert_eq!(result.key,"hello");
        assert_eq!(result.value,RespFrame::BulkString(b"world".into()));
        Ok(())
    }

    #[test]
    fn test_set_get_command() -> Result<()>{
        let backend = Backend::new();
        let set_cmd = Set{
            key: "mykey".to_string(),
            value: RespFrame::BulkString(b"myvalue".into()),
        };
        let set_resp = set_cmd.execute(&backend);
        assert_eq!(set_resp,*RESP_OK);

        let get_cmd = Get{
            key: "mykey".to_string(),
        };
        let get_resp = get_cmd.execute(&backend);
        assert_eq!(get_resp,RespFrame::BulkString(b"myvalue".into()));

        let get_cmd2 = Get{
            key: "nokey".to_string(),
        };
        let get_resp2 = get_cmd2.execute(&backend);
        assert_eq!(get_resp2,RespFrame::Null(crate::RespNull));

        Ok(())
    }
}